#pragma once 

#include <list>
#include <mutex>
#include <memory>
#include <condition_variable>
#include <thread>
#include "online.hpp"
#include "room.hpp"
#include "db.hpp"

template<class T>
class matcher_queue{
private:
    /*这里需要用链表，不使用的queue的原因是因为我们中间有删除的操作*/
    std::list<T> _list;
    /*保证线程安全*/
    std::mutex _mutex;
    /*这个条件变量主要是用来阻塞消费者,后面使用的时候：队列中的元素<2 则阻塞*/
    std::condition_variable _cond;
public:
    /*获取元素个数*/
    size_t size(){
        return _list.size();
    }
    /*判断是否为空*/
    bool empty(){
        return _list.empty();
    }
    /*阻塞线程*/
    void wait(){
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
    /*入队列，并且唤醒线程*/
    void push(T& in){
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(in);
        _cond.notify_all();
    }
    /*出队数据*/
    bool pop(T& out){
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty()==true){
            return false;
        }
        out=_list.front();
        _list.pop_front();
        return true;
    }

    /*移除指定的数据*/
    void remove(T& data){
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
};



class matcher{
private:
    /*普通选手匹配队列*/
    matcher_queue<uint64_t> _q_normal;
    /*高手匹配队列*/
    matcher_queue<uint64_t> _q_hight;
    /*大神匹配队列*/
    matcher_queue<uint64_t> _q_super;

    /*对应的三个匹配队列的线程*/
    std::thread _th_normal;
    std::thread _th_hight;
    std::thread _th_super;
    
    user_table* _ut;
    online_manager* _om;
    room_manager* _rm;
private:
    void handle_matcher(matcher_queue<uint64_t>&mq){
        while(1){
            //1.当队列元素中的元素<2 的时候，阻塞等待，当队列中元素>=2的时候，开始为这两个玩家创建对应的房间
            while(mq.size()<2){
                mq.wait();
            }
            //2.走到这说明队列中至少有2个玩家
            uint64_t uid1,uid2;
            bool ret=mq.pop(uid1);
            if(ret==false){
                continue;
            }
            ret=mq.pop(uid2);
            if(ret==false){
                //没有取出第二玩家，需要重新将玩家1添加回队列
                this->add(uid1);
                continue;;
            }
            //3. 校验两个玩家是否在线，如果有⼈掉线，则要吧另⼀个⼈重新添加⼊队列
            wsserver_t::connection_ptr conn1=_om->get_conn_from_game_hall(uid1);
            if(conn1.get()==nullptr){
                this->add(uid2);
                continue;
            }
            wsserver_t::connection_ptr conn2=_om->get_conn_from_game_hall(uid2);
            if(conn2.get()==nullptr){
                this->add(uid1);
                continue;
            }
            //4. 为两个玩家创建房间，并将玩家加⼊房间中
            room_ptr rp=_rm->create_room(uid1,uid2);
            if(rp.get()==nullptr){
                this->add(uid1);
                this->add(uid2);
                continue;
            }

            //5.对玩家进行响应
            Json::Value json_resp;
            json_resp["optype"]="match_success";
            json_resp["result"]=true;
            std::string body;
            json_util::serialize(json_resp,body);
            conn1->send(body);
            conn2->send(body);
        }
    }

    void th_normal_entry(){return handle_matcher(_q_normal);}
    void th_hight_entry(){return handle_matcher(_q_hight);}
    void th_super_entry(){return handle_matcher(_q_super);}

public:
    matcher(user_table* ut,online_manager*om,room_manager* rm)
    :_ut(ut),_om(om),_rm(rm),
    _th_normal(std::thread(&matcher::th_normal_entry,this)),
    _th_hight(std::thread(&matcher::th_hight_entry,this)),
    _th_super(std::thread(&matcher::th_super_entry,this)){
        DBG_LOG("游戏匹配管理模块初始化完毕！！");
    }
    ~matcher(){
        DBG_LOG("游戏匹配管理模块即将销毁！！");
    }
    bool add(uint64_t uid){
        //根据玩家的天梯分数，来判断玩家的档次，添加到对应的匹配队列
        Json::Value user;
        //1. 根据玩家id获取分数
        bool ret=_ut->select_by_id(uid,user);
        if(ret==false){
            DBG_LOG("获取玩家%d 信息失败 ",uid);
            return false;
        }
        int score=user["score"].asInt();
        //2. 根据玩家分数，添加到对应的匹配队列
        if(score<2000){
            _q_normal.push(uid);
        }else if(score<3000){
            _q_hight.push(uid);
        }else{
            _q_super.push(uid);
        }
        return true;
    }

    bool del(uint64_t uid){
        Json::Value user;
        bool ret=_ut->select_by_id(uid,user);
        if(ret==false){
            DBG_LOG("获取玩家%d 信息失败 ",uid);
            return false;
        }
        int score=user["score"].asInt();
        //根据玩家分数，从对应的匹配队列中移除
        if(score<2000){
            _q_normal.remove(uid);
        }else if(score<3000){
            _q_hight.remove(uid);
        }else{
            _q_super.remove(uid);
        }
        return true;
    }
};